// 8.3 string流
/**
 * sstream头文件定义了三个类型来支持内存IO，这些类型可以向string写入数据，从string读取数据，就像string是一个IO流一样。
 * 1. istringstream从string读取数据
 * 2. ostringstream向string写入数据
 * 3. 而头文件stringstream既可从string读数据也可向string写数据。
 */

#include <vector>
#include <iostream>
#include <sstream>
using std::cerr, std::clog;
using std::cin, std::cout, std::endl, std::flush, std::ends, std::unitbuf, std::nounitbuf;
using std::istringstream, std::ostringstream;
using std::ostream, std::istream;
using std::string, std::vector;

// 8.3.1 使用istringstream
/*
    当我们的某些工作是对整行文本进行处理，而其他一些工作是处理行内的单个单词时，通常可以使用istringstream。
*/
struct PersonInfo
{
    string name;
    vector<string> phones;
};

int main()
{
    string line, word;         // 分别保存来自输入的一行和单词
    vector<PersonInfo> people; // 保存来自输入的所有记录
    // 逐行从输入读取数据，直至cin遇到文件尾（或其他错误）
    while (getline(cin, line))
    {
        PersonInfo info;            // 创建一个保存此记录数据的对象
        istringstream record(line); // 将记录绑定到刚读入的行
        record >> info.name;        // 读取名字
        while (record >> word)      // 读取电话
        {
            info.phones.push_back(word);
        }
        people.push_back(info); // 追加到people末尾
    }

    // 8.3.2 使用ostringstream
    // 我们假定已有两个函数，valid和format，分别完成电话号码验证和改变格式的功能。
    for (const auto &entry : people) // 循环people中的每一项
    {
        ostringstream formatted, badNums;     // 每个循环步骤创建的对象
        for (const auto &nums : entry.phones) // 循环每个电话号
        {
            if (!valid(nums)) //
            {
                badNums << " " << nums; // 将不符合的电话号码存入badNums
            }
            else
            {
                formatted << " " << format(nums); // 将格式化后的电话号码存入formatted
            }
        }
        if (badNums.str().empty()) // 如果没有错误的电话号码
        {
            os << entry.name << " " << formatted.str() << endl; // 打印名字和格式化后的电话号码
        }
        else // 否则打印名字和错误的电话号码
        {
            os << "input error: " << entry.name << " invalid number(s) "
               << badNums.str() << endl;
        }
    }

    return 0;
}